home *** CD-ROM | disk | FTP | other *** search
- /*
- File: Lists.c
-
- Contains: List Manager stuff and associated routines
-
- Written by: Chris White, Developer Technical Support
-
- Copyright: © 1995 by Apple Computer, Inc., all rights reserved.
-
- Change History (most recent first):
-
- 9/28/95 CW First release
-
- */
-
- #include <Drag.h>
- #include <Errors.h>
- #include <MixedMode.h>
-
-
- #include "MenuStuff.h"
-
- #include "Prototypes.h"
-
-
- static Boolean UserWantsToDrag ( ListRef theList, Point globallPt );
- static Boolean MouseHitSelection ( ListRef theList, Point localPt );
- static Boolean EqualCells ( Cell c1, Cell c2 );
- static void HandleClickInList ( WindowRef theWindow, Boolean newClick,
- Boolean doubleClick, Cell newCell );
- static Boolean GetSelectionRect ( ListRef theList, Rect* theRect );
- static OSErr StartADrag ( WindowRef theWindow, EventRecord* theEvent );
- static OSErr AddFlavors ( DragReference theDrag, ItemReference theItem, WindowRef theWindow );
-
- static void OutlineRegion ( RgnHandle theRgn );
- static void LocalToGlobalRect ( Rect* theRect );
-
- static Boolean MyClickLoop ( void );
-
-
-
-
-
- // We'll limit its scope to this file
- static ListRef gCurrentClickedList = nil;
-
-
- //
- // Gotcha: The List Manager doesn't actually check D0, but rather simply tests the Z-bit in
- // the 68K processor's status register. Because this is lost during Mixed Mode, we need to
- // use a 68K stub routine that calls the native clickLoop and then tests DO before returning
- // to the List Manager. The emulator will set its Z-bit, and the List Manager will be happy.
- // See the ReadMe file for a more complete explanation.
- //
- void InitListClickLoop ( void )
- {
-
- #ifdef powerc
-
- static RoutineDescriptor theListClickLoopRD =
- BUILD_ROUTINE_DESCRIPTOR ( uppListClickLoopProcInfo, MyClickLoop );
-
-
- #ifdef powerc
- #pragma options align=mac68k
- #endif
-
- static struct LClickLoopGlue
- {
- long move; // MOVEA.L ClickLoopUPP, A0
- short jsr; // JSR (A0)
- short tst; // TST.B D0
- short rts; // RTS
- UniversalProcPtr ClickLoopUPP; // Storage for the UPP
- } LClickLoop68K = {
- 0x207A0008,
- 0x4E90,
- 0x4A00,
- 0x4E75,
- (UniversalProcPtr) &theListClickLoopRD
- };
-
- #ifdef powerc
- #pragma options align=reset
- #endif
-
- gClickLoopUPP = (ListClickLoopUPP) &LClickLoop68K;
-
- #else
- gClickLoopUPP = NewListClickLoopProc ( MyClickLoop );
- #endif
-
- return;
- }
-
-
-
- Boolean PtInList ( Point localPt, ListRef theList )
- {
- return PtInRect ( localPt, &(*theList)->rView );
- }
-
-
-
- void GetListRect ( Rect* theRect, ListRef theList )
- {
- *theRect = (*theList)->rView;
- }
-
-
-
- void AddToList ( ListRef theList, Str255 theString )
- {
- Rect dataRect;
- Cell cell;
- short nuRow;
- short stringSize = sizeof(OSType);
-
- #if DEBUGGING
- if ( theList == nil ) DebugStr ( "\p theList == nil");
- #endif
-
- dataRect = (*theList)->dataBounds;
- nuRow = LAddRow ( 1, dataRect.bottom, theList );
- SetPt ( &cell, 0, nuRow );
- LSetCell ( &theString[1], theString[0], cell, theList );
-
- return;
- }
-
-
-
- void UpdateList ( ListRef theList, int16 theIndex, Str255 theString )
- {
- Cell theCell;
-
- SetPt ( &theCell, 0, theIndex );
- LSetCell ( &theString[1], theString[0], theCell, theList );
-
- return;
- }
-
-
-
- void DeleteFromList ( ListRef theList, int16 theIndex )
- {
- LDelRow ( 1, theIndex, theList );
-
- return;
- }
-
-
- /*------------------------------------------------------------------*/
-
-
-
- Boolean HandleListClick ( WindowRef theWindow, EventRecord* event )
- {
- Boolean bWasHandled = false;
- Point localPt;
- Rect listRect;
- ListRef theList;
-
-
-
- localPt = event->where;
- GlobalToLocal ( &localPt );
- theList = GetWListRef ( theWindow );
-
-
- if ( (*theList)->lActive == true )
- {
- listRect = (*theList)->rView;
- listRect.right += 15; listRect.bottom += 15;
-
- if ( PtInRect ( localPt, &listRect ) )
- {
- Cell oldCell, newCell;
- Boolean oldSelection,
- validSelection = false,
- newClick = false,
- doubleClick;
-
-
- oldSelection = GetFirstSelection ( theList, &oldCell );
-
-
-
- // UserWantsToDrag takes a point in global coords. It uses the Drag Manager
- // routine WaitMouseMoved to make sure the user is dragging as opposed
- // to double clicking. If a drag is started when the user is really trying
- // to double click, it will end up being a very quick drag.
-
- if ( UserWantsToDrag ( theList, event->where ) )
- {
-
- // The click loop code needs to wait until the mouse is moved out
- // of the list rect before starting a drag. Because the default
- // behaviour of a a drag within a list is to change the selection
- // and, if required, scroll the list, attempting to drag a cell
- // by moving the mouse over another cell will result in that cell
- // being hilited. The job of this code is to detect those attempted
- // drags and call the necessary drag routines instead of making a
- // call to the LClick routine.
-
-
- // If we successfully drop the cell somewhere else,
- // clear the selection in the original list.
-
- if ( StartADrag ( theWindow, event ) == noErr )
- LSetSelect ( false, oldCell, theList );
-
- return true;
-
- }
-
- //
- // Save the current list for use in our click loop
- //
- gCurrentClickedList = theList;
- doubleClick = LClick ( localPt, event->modifiers, theList );
-
- validSelection = GetFirstSelection ( theList, &newCell );
- if ( validSelection )
- if ( !oldSelection || !EqualCells ( newCell, oldCell ) )
- newClick = true;
-
- HandleClickInList ( theWindow, newClick, doubleClick, newCell );
- bWasHandled = true;
- }
- }
-
- return bWasHandled;
- }
-
-
-
- Boolean GetSelection ( ListRef theList, int16* theIndex )
- {
- Boolean bFound;
- Cell theCell;
-
- SetPt ( (Point*) &theCell, 0, *theIndex );
- bFound = LGetSelect ( true, &theCell, theList );
- if ( bFound )
- *theIndex = theCell.v;
-
- return bFound;
- }
-
-
-
- Boolean GetFirstSelection ( ListRef theList, Cell* theCell )
- {
- SetPt ( (Point*) theCell, 0, 0 );
- return LGetSelect ( true, theCell, theList );
- }
-
-
-
- //
- // Does the user want to drag something? First, checks the click could
- // be drag, then waits to see if the user starts to drag the cell.
- //
- static Boolean UserWantsToDrag ( ListRef theList, Point globalPt )
- {
- Point localPt;
-
- localPt = globalPt;
- GlobalToLocal ( &localPt );
- if ( MouseHitSelection ( theList, localPt ) )
- return WaitMouseMoved ( globalPt );
-
- return false;
- }
-
-
-
- //
- // Has the user clicked on a select cell?
- //
- static Boolean MouseHitSelection ( ListRef theList, Point localPt )
- {
- Cell selectedCell;
- Rect selectedCellRect;
-
-
- if ( !(GetFirstSelection ( theList, &selectedCell )) )
- return false;
-
- if ( !(PtInRect ( *(Point*) &selectedCell, (Rect*) &(*theList)->visible )) )
- return false;
-
- if ( !(GetSelectionRect ( theList, &selectedCellRect )) )
- return false;
-
- return PtInRect ( localPt, &selectedCellRect );
- }
-
-
-
- static Boolean EqualCells ( Cell c1, Cell c2 )
- {
- return (c1.h == c2.h && c1.v == c2.v);
- }
-
-
-
- //
- // This routine is called in response to a click in a list. We'll
- // turn a double click into a “Get Info” command.
- //
- static void HandleClickInList ( WindowRef theWindow, Boolean newClick,
- Boolean doubleClick, Cell newCell )
- {
- if ( doubleClick )
- {
- int32 menuSelection;
-
- menuSelection = kFragmentMenu << 16;
- menuSelection += cGetInfo;
- MenuDispatch ( menuSelection );
- }
-
- return;
- }
-
-
-
- //
- // This gets a rectangle for the selection in a list. It's used to
- // pass to the Drag Manager for user feedback.
- //
- static Boolean GetSelectionRect ( ListRef theList, Rect* theRect )
- {
- Boolean validSelection;
- Cell selectedCell, adjustedCell;
- Rect selectedCellRect, emptyRect = {0, 0, 0, 0};
-
- *theRect = emptyRect;
- validSelection = GetFirstSelection ( theList, &selectedCell );
-
- if ( validSelection )
- {
-
- //
- // adjustedCell is needed because the current view of the list may not
- // have cell (0, 0) at the top left corner. This takes the top and left
- // values of the visible field and subtracts them from the selected cell.
- // We end up with this cell's location in the current view honoring scroll
- // bars.
- //
-
- adjustedCell.v = selectedCell.v - (*theList)->visible.top;
- adjustedCell.h = selectedCell.h - (*theList)->visible.left;
-
- selectedCellRect.top = (*theList)->rView.top + (adjustedCell.v * (*theList)->cellSize.v);
- selectedCellRect.bottom = selectedCellRect.top + (*theList)->cellSize.v;
-
-
- //
- // Because the cell width is 32767 (see comments in CreateContentList routine),
- // we need to use the view width instead of the cell width. If we don't, the Drag
- // Manager will not draw anything as the user drags the cell around (because it's
- // too big). Even if the Drag Manager did do any drawing, it wouldn't reflect the
- // size of the cell as the user sees it.
- //
-
- selectedCellRect.left = (*theList)->rView.left + (adjustedCell.h * (*theList)->rView.right);
- selectedCellRect.right = selectedCellRect.left + (*theList)->rView.right;
-
- *theRect = selectedCellRect;
- }
-
- return validSelection;
- }
-
-
-
- //
- // This creates a new drag for the Drag Manager and tracks it until
- // the user is done. If a successful drop is made, the Drag Manager
- // will call our DragReceiver routine.
- //
- static OSErr StartADrag ( WindowRef theWindow, EventRecord* theEvent )
- {
- OSErr theErr = noErr;
- DragReference theDrag;
- Rect dragBounds;
- RgnHandle dragRgn;
- ListRef theList;
- ItemReference theItem = 1;
-
-
- if ( !gHasDragManager )
- return kDragManagerNotPresent;
-
-
- theList = GetWListRef ( theWindow );
-
- theErr = NewDrag ( &theDrag );
- if ( theErr == noErr )
- {
- theErr = AddFlavors ( theDrag, theItem, theWindow );
-
- if ( theErr == noErr )
- {
-
- if ( GetSelectionRect ( theList, &dragBounds ) )
- {
- LocalToGlobalRect ( &dragBounds );
- theErr = SetDragItemBounds ( theDrag, theItem, &dragBounds );
-
- if ( theErr == noErr )
- {
- dragRgn = NewRgn ( );
- RectRgn ( dragRgn, &dragBounds );
- OutlineRegion ( dragRgn );
- theErr = TrackDrag ( theDrag, theEvent, dragRgn );
-
- DisposeRgn ( dragRgn );
- }
- }
- }
-
- DisposeDrag ( theDrag );
- }
-
- return theErr;
- }
-
-
-
- static Boolean MyClickLoop ( void )
- {
- Point localPt;
- Cell selectedCell;
- WindowRef theWindow;
- ListRef list;
-
-
-
- //
- // This code starts a drag for those cases where there isn't already
- // a selection, and the mouse is dragged out in such a way as to not
- // cause another cell to be hilited. We need to wait until the mouse
- // is dragged out of the list rect for two reasons. First, to allow
- // the default behaviour to occur. Second, to stop a single click from
- // acting like the current selection was dragged and release where the
- // click was made. Note that the default behaviour is to change the
- // selection as the mouse is dragged over the cells, and scroll the
- // list if required.
- //
-
-
- theWindow = FrontWindow ( );
- list = GetWListRef ( theWindow );
-
- GetMouse ( &localPt );
- if ( GetFirstSelection ( list, &selectedCell ) )
- {
- if ( !(PtInRect ( localPt, &(*list)->rView)) )
- {
- EventRecord dummyEvent;
- long tmpLong;
- Rect tmpRect;
-
- OSEventAvail(0, &dummyEvent);
- dummyEvent.what = mouseDown;
-
- //
- // We go through the trouble of making sure the mouse doesn't
- // appear outside of the list cell region when the drag starts.
- //
-
- GetSelectionRect(list, &tmpRect);
- InsetRect(&tmpRect, 2, 2);
- tmpLong = PinRect(&tmpRect, localPt);
- dummyEvent.where = *(Point *) &tmpLong;
- LocalToGlobal((Point *) &dummyEvent.where);
-
- //
- // If we successfully drop the cell somewhere else,
- // clear the selection in the original list.
- //
-
- if ( StartADrag ( theWindow, &dummyEvent ) == noErr )
- LSetSelect ( false, selectedCell, list );
-
- return false;
-
- }
- }
-
- return true;
- }
-
-
-
- //
- // This tells the Drag Manager what data to send when the user drags something
- //
- static OSErr AddFlavors ( DragReference theDrag, ItemReference theItem, WindowRef theWindow )
- {
- OSErr theErr = noErr;
- int16 theIndex = 0;
- Size theSize;
- ListRef theList;
- tDragData theData;
-
-
- // First, we'll promise to create a file if the user drags
- // something out to the Finder.
- theErr = AddHFSPromise ( theDrag, theItem );
- if ( theErr )
- return theErr;
-
- // Set the send procedure that will deliver any promised data.
- SetDragSendProc ( theDrag, gDragSendDataProcUPP, theWindow );
-
-
-
- //
- // I'm being lazy here. There's no real advantage to including
- // the window reference with each item (well, apart from the effort
- // saved on my part).
- //
-
- theList = GetWListRef ( theWindow );
- theData.theWindow = theWindow;
- theSize = sizeof ( tDragData );
-
- while ( GetSelection ( theList, &theIndex ) )
- {
- OSErr theErr;
-
- //
- // The flavorSenderOnly is used to tell the Drag Manager that this data will not
- // be valid in another client. Eg. If this application was launched twice, one
- // could accept a drag from the other. However, since the data consists of ptrs
- // rather than the actual data, it wouldn't make sense. Note, this doesn't affect
- // dragging to the finder, which is a special case.
- //
- theData.theIndex = theIndex;
- theErr = AddDragItemFlavor ( theDrag, theItem, kCreatorCode,
- (Ptr) &theData, theSize, flavorSenderOnly );
- if ( theErr )
- return theErr;
-
- theIndex++;
- }
-
-
- return noErr;
- }
-
-
-
- static void OutlineRegion ( RgnHandle theRgn )
- {
- RgnHandle tempRgn;
-
- tempRgn = NewRgn ( );
- CopyRgn ( theRgn, tempRgn );
- InsetRgn ( tempRgn, 1, 1 );
- DiffRgn ( theRgn, tempRgn, theRgn );
- DisposeRgn ( tempRgn );
-
- return;
- }
-
-
-
- static void LocalToGlobalRect ( Rect* theRect )
- {
- LocalToGlobal ( (Point*) &theRect->top );
- LocalToGlobal ( (Point*) &theRect->bottom );
-
- return;
- }
-
-
-
-
-